#!/usr/bin/perl -w

############################################################################
#
# Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
# Contact: http://www.qt-project.org/legal
#
# This file is part of Qt Creator.
#
# Commercial License Usage
# Licensees holding valid commercial Qt licenses may use this file in
# accordance with the commercial license agreement provided with the
# Software or, alternatively, in accordance with the terms contained in
# a written agreement between you and Digia.  For licensing terms and
# conditions see http://qt.digia.com/licensing.  For further information
# use the contact form at http://qt.digia.com/contact-us.
#
# GNU Lesser General Public License Usage
# Alternatively, this file may be used under the terms of the GNU Lesser
# General Public License version 2.1 as published by the Free Software
# Foundation and appearing in the file LICENSE.LGPL included in the
# packaging of this file.  Please review the following information to
# ensure the GNU Lesser General Public License version 2.1 requirements
# will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
#
# In addition, as a special exception, Digia gives you certain additional
# rights.  These rights are described in the Digia Qt LGPL Exception
# version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
#
############################################################################

use strict;
use Getopt::Long;
use IO::File;

my $optDryRun = 0;
my $optHelp = 0;
my $optPreffix ='';
my $optName = '';
my $optParentType = '';
my $optProjectName = '';
my $optDescription = '';

my $optCppSourceSuffix = 'c';
my $optCppHeaderSuffix = 'h';

# -- Read in a file and return its lines
sub readFile
{
    my ($fileName) = @_;
    my @rc = ();
    my $fh = new IO::File('<' . $fileName) or die ('Unable to open for reading ' .  $fileName . ' :' . $!);
    while (my $line = <$fh>) {
	chomp($line);
        push (@rc, $line);
    }
    $fh->close();
    return @rc;
}

my $USAGE=<<EOF;
Usage: generate.pl [--help] | [--dry-run]
                   [--preffix=<Preffix>]
                   [--name=<The name of the new type, with words separated by '_'>]
                   [--parent-type=<The GType of the parent type>]
                   [--project-name=<The project name>]
                   [--description=<The description license>]

GObject wizard project generation script.

EOF

my $argCount = scalar(@ARGV);
if ($argCount == 0
    || !GetOptions("help" => \$optHelp,
                   "dry-run" => \$optDryRun,
                   "preffix:s" => \$optPreffix,
                   "name:s" => \$optName,
                   "parent-type:s" => \$optParentType,
                   "project-name:s" => \$optProjectName,
                   "description:s" => \$optDescription)
    || $optHelp != 0) {
    print $USAGE;
    exit (1);
}
# gtk, application_window, GTK_TYPE_WIDGET
# gtkapplicationwindow.c
# GtkApplicationWindow

# gtk
# Gtk
# GTK
# gtk-my.c
# GTK_IS_MY

# -- Determine file names : WebInk, DOMDocument
# ----------------------------------------------
sub namespace_lc {
	
}

my $structNamePreffix = $optPreffix; # WebKit
my $structNameBase = $optName;       # DOMDocument
#                WebKitDOMDocument
my $structName = $structNamePreffix . $structNameBase;

my $functionNamePreffix = $optPreffix;# WebKit
$functionNamePreffix = lc($functionNamePreffix);
my $functionNameBase = $optName;# DOMDocument
$functionNameBase =~ s/([A-Z])/_$1/g;
#                  _Web_D_O_M_Document
$functionNameBase =~ s/_([A-Z])_/-$1_/g;
#                  _Web-D-O-M_Document

$functionNameBase = lc($functionNameBase);
#                  webkit_dom_document
my $functionName = $functionNamePreffix . '*' . $functionNameBase;



# ----------------------------------------------
# gtk,    DOMDOcument  => GtkDOMDocument,    gtk_dom_document
# WebKit, DOMDOcument  => WebKitDOMDocument, webkit_dom_document
# webkit, DOMDOcument  => WebkitDOMDocument, webkit_dom_document
# file_name;
# file_preffix;
# file_separator; => '-'     gtk-document.c
# class_separator; => ''     GtkDocument
# function_separator; => '_' gtk_document()
# class_name;
# class_preffix;
# function_name;
# function_preffix;
# 
# preffix :
# Gtk
# 
# name :
# DOMDocument
# 
# parent-type :
# G_TYPE_OBJECT / GObject
# 
# ----------------------------------------------


my $preffix  = '';
if ($optPreffix ne '') {
    $preffix = lc($optPreffix) . '_';
}

my $baseName = $optName;
$baseName =~ tr/-/_/;

my $baseFileName = lc($preffix) . lc($baseName);
my $funcName = lc($baseFileName);
my $className = $optPreffix . '_' . $baseName;
# my $className = '_' . lc($baseFileName);
$className =~ s/_([a-zA-Z])/\u$1/g;
$baseFileName =~ s/_//g;

my $sourceFileName = $baseFileName . '.' . $optCppSourceSuffix;
my $headerFileName = $baseFileName . '.' . $optCppHeaderSuffix;
# my $mainSourceFileName = 'main.' . $optCppSourceSuffix;
# my $projectFileName = lc($optProjectName) . '.pro';

my %g_types = ('G_TYPE_OBJECT', 'GObject');

my %gtk_types = ('GTK_TYPE_WIDGET',    'GtkWidget',
                 'GTK_TYPE_STATUSBAR', 'GtkStatusbar');

my %new_types = ('G_TYPE_OBJECT',      'GObject',
                 'GTK_TYPE_WIDGET',    'GtkWidget',
                 'GTK_TYPE_STATUSBAR', 'GtkWidget');

my %macro_types = ('G_TYPE_OBJECT',      'G_OBJECT',
                   'GTK_TYPE_WIDGET',    'GTK_WIDGET',
                   'GTK_TYPE_STATUSBAR', 'GTK_STATUSBAR');

my $parentType = "";
if(exists($g_types{$optParentType})) {
	$parentType = $g_types{$optParentType};
} else {
	if(exists($gtk_types{$optParentType})) {
		$parentType = $gtk_types{$optParentType};
	} else {
		print 'Unexcepted parent-type :"', $optParentType, "\"\n";
		print "\t", 'Use a valide type : glib{GObject}, gtk{GtkWidget, ...}', "\n";
		exit;
	}
}
my $newType = $new_types{$optParentType};
my $macroType = $macro_types{$optParentType};


if ($optDryRun) {
#   -- Step 1) Dry run: Print file names along with attributes
    print $sourceFileName,",openeditor\n";
    print $headerFileName,",openeditor\n";
#    print $mainSourceFileName,",openeditor\n";
#    print $projectFileName,",openproject\n";
} else {
#   -- Step 2) Actual file creation
    print 'Generating ',  $headerFileName, ' ', $sourceFileName, "\n";
    
    my $headerFile = new IO::File('>' . $headerFileName) or die ('Unable to open ' . $headerFileName . ' :' . $!);
    print $headerFile '#ifndef ', '__', uc($preffix), uc($optName), '_H__', "\n";
    print $headerFile '#define ', '__', uc($preffix), uc($optName), '_H__', "\n";
    
    print $headerFile "\n";
    
    print $headerFile '#include <gtk/gtk.h>', "\n";
    
    print $headerFile "\n";
    
    print $headerFile 'G_BEGIN_DECLS', "\n\n";
    
    print $headerFile '#define ', uc($preffix), 'TYPE_', uc($optName), '            ', '(', lc($preffix), lc($optName), '_get_type()', ')', "\n";
    print $headerFile '#define ', uc($preffix), uc($optName), '(obj)            ', '(', 'G_TYPE_CHECK_INSTANCE_CAST ((obj), ', uc($preffix), 'TYPE_', uc($optName), ', ', $className, '))', "\n";
    print $headerFile '#define ', uc($preffix), uc($optName), '_CLASS(klass)', '    ', '(', 'G_TYPE_CHECK_CLASS_CAST ((klass),  ', uc($preffix), 'TYPE_', uc($optName), ', ', $className, 'Class))', "\n";
    print $headerFile '#define ', uc($preffix), 'IS_', uc($optName), '(obj)         ', '(', 'G_TYPE_CHECK_INSTANCE_TYPE ((obj), ', uc($preffix), 'TYPE_', uc($optName), '))', "\n";
    print $headerFile '#define ', uc($preffix), 'IS_', uc($optName), '_CLASS(klass)', ' ', '(', 'G_TYPE_CHECK_CLASS_TYPE ((klass),  ', uc($preffix), 'TYPE_', uc($optName), '))', "\n";
    print $headerFile '#define ', uc($preffix), uc($optName), '_GET_CLASS(obj)', '  ', '(', 'G_TYPE_INSTANCE_GET_CLASS ((obj),  ', uc($preffix), 'TYPE_', uc($optName), ', ',  $className, 'Class))', "\n";

    print $headerFile "\n";
    
	# test
    #print $headerFile 'typedef struct ', $functionName, ";\n";
    #print $headerFile 'typedef struct ', $structName, ";\n";

    print $headerFile 'typedef struct _', $className, ' ', $className, ";\n";
    print $headerFile 'typedef struct _', $className, 'Class ', $className, 'Class', ";\n";
    
    print $headerFile "\n";
    
    print $headerFile 'struct _', $className, ' {', "\n";
    print $headerFile "\t", $parentType, ' parent_instance;', "\n";
    print $headerFile '};', "\n";
    
    print $headerFile "\n";
    
    print $headerFile 'struct _', $className, 'Class {', "\n";
    print $headerFile "\t", $parentType, 'Class parent_class;', "\n";
    print $headerFile '};', "\n";

    print $headerFile "\n";
    
    print $headerFile 'GType ', lc($preffix), lc($optName), '_get_type();', "\n";
    print $headerFile $newType, ' *', lc($preffix), lc($optName), '_new();', "\n";

    print $headerFile "\n";
    
    print $headerFile 'G_END_DECLS', "\n";
    
    print $headerFile "\n";
    
    print $headerFile '#endif ', '/* __', uc($preffix), uc($optName), '_H__ */', "\n";

    $headerFile->close();

    
    
    
    
    my $sourceFile = new IO::File('>' . $sourceFileName) or die ('Unable to open ' . $sourceFileName . ' :' . $!);
    
    print $sourceFile  '#include "', $headerFileName ,"\"\n\n";
    

    print $sourceFile  'static void ', $funcName, '_class_init(', $className, 'Class *klass);', "\n";
    print $sourceFile  'static void ', $funcName, '_init(', $className, ' *gobject);', "\n";
    
    print $sourceFile  "\n";
    
    print $sourceFile  'static gpointer ', $funcName, '_parent_class = NULL;', "\n";

    print $sourceFile  "\n";
    
    print $sourceFile  'GType', "\n";
    print $sourceFile  $funcName, '_get_type(void)', "\n";
    print $sourceFile  '{', "\n";
    print $sourceFile  "\t", 'static GType ', $funcName, '_type = 0;', "\n";
    print $sourceFile  "\n";
    print $sourceFile  "\t", 'if (!', $funcName, '_type) {', "\n";
    print $sourceFile  "\t\t", 'static const GTypeInfo ', $funcName, '_info = {', "\n";
    print $sourceFile  "\t\t\t", 'sizeof (', $className, 'Class),   /* class_size */', "\n";
    print $sourceFile  "\t\t\t", 'NULL,                   /* base_init */', "\n";
    print $sourceFile  "\t\t\t", 'NULL,                   /* base_finalize */', "\n";
    print $sourceFile  "\t\t\t", '(GClassInitFunc) ', $funcName, '_class_init,                   /* class_init */', "\n";
    print $sourceFile  "\t\t\t", 'NULL,                   /* class_finalize */', "\n";
    print $sourceFile  "\t\t\t", 'NULL,                   /* class_data */', "\n";
    print $sourceFile  "\t\t\t", 'sizeof (', $className, '),        /* instance_size */', "\n";
    print $sourceFile  "\t\t\t", '0,                      /* n_preallocs */', "\n";
    print $sourceFile  "\t\t\t", '(GInstanceInitFunc) ', $funcName, '_init,                   /* instance_init */', "\n";
    print $sourceFile  "\t\t\t", 'NULL                    /* value_table */', "\n";
    print $sourceFile  "\t\t", '};', "\n";
    
    print $sourceFile  "\t\t", $funcName, '_type = g_type_register_static(', $optParentType, ', "', $className, '", &', $funcName, '_info, 0);', "\n";
    print $sourceFile  "\t", '}', "\n";
    
    print $sourceFile  "\t", 'return ', $funcName, '_type;', "\n";
    print $sourceFile  '}', "\n";
    
    print $sourceFile  "\n";
    
    print $sourceFile  'static void', "\n";
    print $sourceFile  $funcName, '_class_init(', $className, 'Class *klass)', "\n";
    print $sourceFile  '{', "\n";
    print $sourceFile  "\t", 'GtkWidgetClass *widget_class;', "\n";
    print $sourceFile  "\t", 'GObjectClass *object_class;', "\n";
    print $sourceFile  "\n";
    print $sourceFile  "\t", 'widget_class = (GtkWidgetClass *) klass;', "\n";
    print $sourceFile  "\t", 'object_class = (GObjectClass *) klass;', "\n";
    print $sourceFile  "\n";
    print $sourceFile  "\t", $funcName, '_parent_class = g_type_class_peek_parent (klass);', "\n";
    print $sourceFile  '}', "\n";
    
    print $sourceFile  "\n";
    
    print $sourceFile  'static void', "\n";
    print $sourceFile  $funcName, '_init(', $className, ' *klass)', "\n";
    print $sourceFile  '{', "\n";
    print $sourceFile  '}', "\n";
    
    print $sourceFile  "\n";
    
    print $sourceFile  $newType, ' *', $funcName, '_new()', "\n";
    print $sourceFile  '{', "\n";
    print $sourceFile  "\t", 'return ', $macroType, '(g_object_new (', $funcName, '_get_type(), NULL));', "\n";
    print $sourceFile  '}', "\n";
    
    $sourceFile->close();

    
}